// threadtest.cc
//	Simple test case for the threads assignment.
//
//	Create two threads, and have them context switch
//	back and forth between themselves by calling Thread::Yield,
//	to illustratethe inner workings of the thread system.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "synch.h"

static char* buf = NULL;
static Semaphore* holes = NULL;
static Semaphore* content = NULL;
static Lock* buf_lock = NULL;
static int buf_size = 0;
static int buf_start = 0;
static int buf_end = 0;

static void
producer(int which) {
    char hello[] = "Hello world";

    for(int i = 0; hello[i] != '\0'; i++) {
        holes->Wait();
        buf_lock->Acquire();
        printf("Producer #%d pushes '%c' onto the buffer\n",
               which, hello[i]);
        buf[buf_end] = hello[i];
        buf_end = (buf_end + 1) % buf_size;
        buf_lock->Release();
        content->Signal();
    }
}

static void
consumer(int which) {
    while(true) {
        content->Wait();
        buf_lock->Acquire();
        char consumed = buf[buf_start];
        buf_start = (buf_start + 1) % buf_size;
        printf("Consumer #%d pulls '%c' off the buffer\n",
               which, consumed);
        buf_lock->Release();
        holes->Signal();
    }
}

//----------------------------------------------------------------------
// ThreadTest
//----------------------------------------------------------------------

void
ThreadTest() {
    int num_prod = 2;
    int num_con = 2;
    Thread* threads[num_prod + num_con];

    DEBUG('t', "Entering Producer/Consumer test.");

    buf_size = 50;
    buf = new char[buf_size];
    holes = new Semaphore("holes", buf_size);
    content = new Semaphore("content", 0);
    buf_lock = new Lock("buf_lock");

    for(int i = 0; i < num_prod; i++) {
        threads[i] = new Thread("Procucer");
        threads[i]->Fork(producer, i);
    }
    for(int i = 0; i < num_con; i++) {
        threads[num_prod + i] = new Thread("Consumer");
        threads[num_prod + i]->Fork(consumer, i);
    }
}

